import React, { useState, useEffect, useCallback, FormEvent, useRef, ChangeEvent } from 'react';
import $ from "jquery";
import { debounce } from "underscore";
import "bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
import Modal from "./components/Modal";
import TopBar from "./components/TopBar";
import ChatZone from "./components/ChatZone";
import InputBox from "./components/InputBox";
import SideNav from "./components/SideNav";
import socket from "./socketClient";
import {
  EMIT_USER_COME,
  EMIT_USER_FILE,
  EMIT_USER_LEAVE,
  EMIT_USER_MESSAGE
} from "./tools/emitName";
import {
  ON_USER_COME,
  ON_USER_FILE,
  ON_USER_LEAVE,
  ON_USER_MSG,
  ON_USER_ID,
} from "./tools/onName";
import {
  NEW_FILE,
  NEW_MESSAGE,
  NEW_PERSON,

  STORAGE_NAME
} from "./tools/msgType";

enum NicknameCode{
  NO_CHANGE = 0,      // 用户名没有变化
  NEW_USER_COM = 1,   // 新的用户到来
  USER_RENAME = 2     // 用户更改昵称
};

function App() {

  let [userName, setUserName] = useState<string>("");   // 捕获用户输入的昵称
  let [message, setMessage] = useState<string>("");     // 捕获输入的消息
  let [msgs, setMsgs] = useState<NewMsg[]>([]);     // 这个数组是用来存放消息的。
  let [userList, setUserList] = useState<NicknameData[]>([]);   // 用户列表

  let modalEl = useRef<HTMLDivElement>(null);
  let iptEl = useRef<HTMLInputElement>(null);
  let [userId, setUserId] = useState<NicknameData['userId']>(0);   // 捕获用户的 id

  const setNewMsg = useCallback((
      nickname: string | Array<string>,
      type: string = NEW_PERSON, 
      msg: MsgType = "", 
      isSelf: boolean = false, 
      isLeave:boolean = false,
      filename: string = ""
    ) => {
    msgs.push({
      msg: msg,
      type: type,
      user: nickname,
      isSelf: isSelf,
      date: new Date().toLocaleString(),
      isLeave,
      filename
    });
    setMsgs([...msgs]);
  },[msgs]);

  // 用户点击离开时
  const leave = useCallback(() => {
    // 弹出填写框，给其他人广播这个用户离开了
    $(modalEl.current as HTMLDivElement).modal('show');
    socket.emit(EMIT_USER_LEAVE,{ nickname: userName, userId });
  },[userName, userId]);

  // 事件监听 --------------------------------------------------------------------------------------> 
  useEffect(() => {
    // 把监听函数放在初始化函数里面
    socket.on(ON_USER_ID, (data: AjaxMsg<AjaxCodeData>) => {
      const user = data.data;
      setUserList(user.userList);
      // 用户注册成功，把信息 push 进数组
      if(data.data.code !== NicknameCode.NO_CHANGE){   // 如果用户昵称更改了才发布
        setNewMsg(user.nickname, NEW_PERSON, "", true);
      }
      setUserId(user.userId);   // 设置 userId
      sessionStorage.setItem(STORAGE_NAME, JSON.stringify({ ...user, date: +new Date() }));
      $(modalEl.current as HTMLDivElement).modal('hide');
    });

    // 监听广播来的新人
    socket.on(ON_USER_COME, (data : NewPerson) => {
      // 有新人到来，就把新人信息 push 进数组
      setUserList(data.userList);
      switch(data.code){
        case NicknameCode.NEW_USER_COM:    // 新的用户到来或者用户更改了昵称
        case NicknameCode.USER_RENAME:
          setNewMsg(data.nickname, NEW_PERSON);
        break;
        default: return;
      }
    });
    
    // 监听广播来的消息
    socket.on(ON_USER_MSG, (data: UserMsg) => {
      setNewMsg(data.nickname, NEW_MESSAGE, data.message, false);
    });

    // 监听离开的人
    socket.on(ON_USER_LEAVE,(userInfo: NicknameData) => {
      const { nickname } = userInfo;
      setNewMsg(nickname, NEW_PERSON, "", false, true);
    });

    // 监听发来的文件
    socket.on(ON_USER_FILE, (data: PicMsg) => {
      const { nickname, message, filename } = data;
      setNewMsg(nickname, NEW_FILE, message, false, false, filename);
    });
  }, [setNewMsg]);
  // -------------------------------------------------------------------------------------> 

  // 图片消息，展示自己上传的图片
  const handleFileChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if(files && files.length){
      if(files[0].size / 1024 / 1024 >= 1){
        alert('图片不能超过 1MB 大小');
        return;
      }
      setNewMsg(userName, NEW_FILE,files[0], true);
  
      socket.emit(EMIT_USER_FILE, { message: files[0], nickname: userName, filename: files[0].name, userId});
    }
  },[setNewMsg, userName, userId]);

  const submitNickname = useCallback(() => {
    const str = userName.trim();
    if (!str) {
      alert("昵称不能为空!");
      $(iptEl.current as HTMLInputElement).focus();
    } else {
      if(str.length > 12){
        alert("昵称长度不能超过 24，请换一个");
        $(iptEl.current as HTMLInputElement).val("");
        return;
      }
      socket.emit(EMIT_USER_COME, { nickname: userName, userId });
    }
  }, [userName, userId]);

  const handleInput = debounce(useCallback((e: FormEvent<HTMLInputElement>) => {
    const ipt = e.target as HTMLInputElement;
    setUserName(ipt.value);
  }, []), 300);

  const handleChangeMessage = useCallback((e: ChangeEvent<HTMLInputElement>) => {
    setMessage(e.target.value);
  }, []);

  const handleSubmitMessage = useCallback(() => {
    if(message){
      setNewMsg(userName, NEW_MESSAGE, message, true);
      socket.emit(EMIT_USER_MESSAGE, { id: userId, message, nickname: userName });
      setMessage('');
    }
  }, [message, userId, userName, setNewMsg]);

  return (
    <div className="justify-content-center container-fluid w-auto row h-100" style={{overflowY: "hidden"}}>
      <SideNav userList={userList.filter(item => item.userId !== userId)} />
      <div className="col-md-12 col-lg-8 h-100" id="chatZone">
        <Modal iptRef={iptEl} modalRef={modalEl} submitNickname={submitNickname} handleInput={handleInput} />

        <TopBar leave={leave} userId={userId} nickname={userName} />

        <ChatZone msgList={msgs} />

        <InputBox 
          handleFileChange={handleFileChange}
          message={message} 
          handleChangeMessage={handleChangeMessage} 
          handleSubmitMessage={handleSubmitMessage} 
        />
      </div>
    </div>
  );
}

export default App;
